package com.exam.api.common.utils;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.util.List;
import java.util.Map;

/**
 * @author bcheng
 */
public class LambdaQueryUtil {

    private static Map<String, ColumnCache> columnMap = null;

    private static boolean initColumnMap = false;


    @SafeVarargs
    public static <T> boolean select(List<String> fields, TableFieldInfo fieldInfo, SFunction<T, ?>... columns) {
        if (CollUtil.isNotEmpty(fields) && !fields.contains("*")) {
            return fields.contains(fieldInfo.getProperty());
        }
        for (SFunction<T, ?> column : columns) {
            ColumnCache columnCache = getColumnCache(column);
            if (columnCache.getColumnSelect().equalsIgnoreCase(fieldInfo.getColumn())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 获取 SerializedLambda 对应的列信息，从 lambda 表达式中推测实体类
     * <p>
     * 如果获取不到列信息，那么本次条件组装将会失败
     *
     * @return 列
     * @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常
     */
    public static <T> ColumnCache getColumnCache(SFunction<T, ?> column) {
        LambdaMeta meta = LambdaUtils.extract(column);
        String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
        Class<?> instantiatedClass = meta.getInstantiatedClass();
        tryInitCache(instantiatedClass);
        return getColumnCache(fieldName, instantiatedClass);
    }

    private static void tryInitCache(Class<?> lambdaClass) {
        if (!initColumnMap) {
            columnMap = LambdaUtils.getColumnMap(lambdaClass);
            Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
            initColumnMap = true;
        }
    }

    private static ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
        ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
        Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
                fieldName, lambdaClass.getName());
        columnCache.setMapping(fieldName);
        return columnCache;
    }
}
